import re

class Parser:

	def __init__(self, writer):
		self.__writer = writer
		self.__prevquotelevel = 0
		self.__incode = False
		self.__inWikiCode = False
		self.__inlCode = False
		self.__inEquation = False
		self.__prevlinewasempty = False

	def parse(self, input):
		for line in input.split('\n'):
			self.processLine(line.rstrip('\r'))

		self.__writer.finalise()
	

	def processLine(self, line):
		literal = self.__inEquation or self.__inlCode

		# Detect changed quote level
		m = re.match(">+", line)
		if m and not literal:
			quotelevel = m.end()
			# Cut off the quotation marks and
			# the whitespace that follows them
			line = line[m.end()::].lstrip()
		else:
			quotelevel = 0

		if quotelevel != self.__prevquotelevel:
			while quotelevel < self.__prevquotelevel:
				self.__writer.decreaseQuoteLevel()
				self.__prevquotelevel -= 1
			while quotelevel > self.__prevquotelevel:
				self.__writer.increaseQuoteLevel()
				self.__prevquotelevel += 1

		# Test if this line is a header
		m = re.match("#+", line)
		if m and not literal:
			last = m.end()
			self.__writer.header(last,
					line[last::].strip().rstrip('#').strip())
			return


		# Test if we're in a 4 space code block
		m = re.match(' {4}', line)
		if m and not self.__inWikiCode and not literal:
			if not self.__incode:
				self.__incode = True
				self.__writer.startcode()
		
			self.__writer.line(line[4::])
			return
		else:
			if self.__incode:
				self.__writer.stopcode()
				self.__incode = False

		# Test if we're in a {{{ }}} code block
		if line.rstrip() == '{{{':
			self.__inWikiCode = True
			self.__writer.startcode()
			return
		elif line.rstrip() == '}}}':
			self.__inWikiCode = False
			self.__writer.stopcode()
			return
		if self.__inWikiCode:
			self.__writer.line(line)
			return

		# Detect link labels for reference style links
		m = re.match(' {0,3}\[([A-Za-z0-9]+)\]: {0,1}([^ ]+) *$', line)
		if m and not self.__incode:
			id = m.groups()[0]
			url = m.groups()[1]

			self.__writer.linkLabel(id, url)
			return


		# Detect inline links and reference style links
		# Inline style looks like [The text](http://link.com)
		# Reference style looks like [The text][reference]
		if not self.__incode:
			inline = re.search("\[([^]]*)\]\(([^)]*)\)", line)
			ref = re.search("\[([^]]+)\] {0,1}\[([A-Za-z0-9]+)\]", line)

			if inline:
				match = inline
			elif ref:
				match = ref
			else:
				match = None

			if match:
				before = line[0:match.start()]
				after = line[match.end()::]
				linktext = match.groups()[0]
				target = match.groups()[1]

				if len(before) > 0:
					self.processLine(before)

				if inline:
					self.__writer.link(linktext, target)
				elif ref:
					self.__writer.refLink(linktext, target)

				if len(after) > 0:
					self.processLine(after)
				return

		# Send text and italic / bold stuff
		escNext = False
		dontSend = False

		for c in line:
			literal = self.__inEquation or self.__inlCode

			if c == '\\' and escNext == False and not self.__inEquation:
				escNext = True
				dontSend = True

			if c == '*' and escNext == False and not literal:
				self.__writer.toggleBold()
				dontSend = True

			if c == '_' and escNext == False and not literal:
				self.__writer.toggleItalic()
				dontSend = True

			if c == '`' and escNext == False and not self.__inEquation:
				self.__writer.toggleInlineCode()
				self.__inlCode = not self.__inlCode
				dontSend = True

			if c == '$' and escNext == False and not self.__inlCode:
				if self.__inEquation:
					self.__inEquation = False
					self.__writer.endequation()
				else:
					self.__inEquation = True
					self.__writer.beginequation()

				dontSend = True

			if dontSend:
				dontSend = False
			else:
				if not self.__inEquation:
					self.__writer.character(c)
				else:
					self.__writer.equationchar(c)
				escNext = False

		# Simulate a newline by a space
		self.__writer.character(' ')
			
		if len(line) > 2 and line[-1] == ' ' and line[-2] == ' ':
			self.__writer.newline()

		# If we enter a sequence of empty lines, 
		# a new paragraph begins
		if len(line) == 0:
			if not self.__prevlinewasempty:
				self.__writer.paragraph()

			self.__prevlinewasempty = True
		else:
			self.__prevlinewasempty = False



